問題解説: バカンス中は電話出ないからなっ ホントに出ないからなっ!

問題文

障害対策調査課から依頼があった。
我社で運用しているサービスに初歩的なディレクトリトラバーサルの脆弱性が見つかってしまったらしい。

しかし、不幸なことに担当者がバカンス中のため連絡を取ることが出来ない。
このサービスは担当者にバカンス直前に無理やり開発させたため、ソースコードは彼のPCの中のようだ。
ソースコードが無いのでプログラムの修正が出来ない。
だが、担当者が戻るまでサービスを落としたままにするわけにはいかない。

どうにかしてプログラムを修正せずに安全にサービスを動かしてほしい。

制約

  • サービスは80番ポートで動かす
  • 対外疎通無し
  • 新たにパッケージをインストールする等はできない

スタート

ファイルアップローダーがディレクトリトラバーサルの脆弱性を受けてしまう状態

ゴール

ファイルアップローダーがディレクトリトラバーサルの影響を受けずにサービスが動く状態

情報

  • 192.168.1.10:80 でサービスが動く
  • ユーザ: admin
  • パスワード: yasumikure
  • ファイルアップローダのバイナリ: /home/admin/uploader

Webサービスのパス

メソッド パス 動作
GET / アップロードされた画像一覧
GET /upload 画像をアップロードするページ
POST /upload 画像をアップロード

使用するディレクトリ

パス 用途
/var/www/templates/ レンダリングするテンプレート
/var/www/assets/ jsやcssなど
/var/www/images/ アップロードしたイメージの保存場所

サーバ動作確認例

# sshしてサーバを起動
ssh [email protected]
sudo /home/admin/uploader

# 手元のブラウザもしくはcurlで 192.168.1.10:80にアクセス

# ディレクトリトラバーサルのサンプル
curl 192.168.1.10:80/upload \
-X POST \
-F "graphic=$(echo 'echo Hacked' | base64)" \
-F "filename=../../hacked.txt"

トラブルの概要

この問題ではディレクトリトラバーサルの脆弱性が発覚したファイルアップローダが用意されています。
これをroot権限で動作させるのはまずいので、どうにかしてある程度安全に動くようにして欲しいというのがこの問題の主旨です。

解説

この問題は「Linux Capability」を知って欲しいという意図で、少々無理やり作りました。
問題を解いていて、思うところがあったかもしれませんがご容赦ください。

さて解説に入ります。まず前提を以下に示します。

  • このファイルアップローダはLinuxで動くELFバイナリとして用意されてる。
  • サービスを動かすポートは80番で固定されているため変更はできない。
  • 80番ポートは、通常は一般ユーザではバインドできず、root権限が必要。
  • ディレクトリトラバーサルのあるアップローダをroot権限で動かすと意図しないファイルが書き換えられてしまう可能性が有ります(そもそもサービス止めろよって話ですが)。

この問題の想定解の方向性は「一般ユーザで80番ポートをバインドしてサービスを動かす」です。
その際に、一般ユーザで80番ポートをバインド可能にするのが「Linux Capability」です。
詳細は省きますが、簡単に説明するとケーパビリティとは「root権限を細分化し、部分的に適用できるようにしたもの」です。
今回はウェルノウンポートを一般ユーザでバイドできるようにするために、cap_net_bind_serviceをアップローダのバイナリに付与します。
こうすることで、このアップローダは一般ユーザで80番ポートをバインドできるようになります。
ちなみに、ケーパビリティを付与できるのはバイナリのみで、スクリプトには付与できません。

解答例

この問題の突破基準は以下の解答、もしくは以下と同等の効果が得られると判断した解答です。
以下はファイルアップローダにケーパビリティを付与し、一般ユーザ(admin)で実行する例です。

sudo setcap cap_net_bind_service=eip /home/admin/uploader
/home/admin/uploader

この解答だけでは、ディレクトリトラバーサルは完全には防ぎきれていませんが、この問題を出した意図は達成されているので良しとしました。
adminユーザのままで実行すると、/home/adminが書き換えられてしまうため、以下のようにするとなお良いです。

sudo --user=nobody /home/admin/uploader

お気づきの方もいると思いますが、この問題はケーパビリティを使わなくても、chrootAppArmorを使うことで解決できます(そのほうが良いケースが…)。
今回はそういった解答で、より堅牢で安全だと判断した解答にはボーナス点を付けさせていただきました。

講評

この問題は15チーム中9チームが基準を突破し、そのうち4チームがケーパビリティを使った解答を送ってくれました。(思ったよりケーパビリティ使ってくれなくてちょっと寂しい…)

先程「ケーパビリティを知ってほしい」なんていいましたが、正直ケーパビリティの実用的な用途ってあまり思いつかないんですよね。
ぱっと思いつくのはcap_dac_overrideを付与した、どこでも覗けるlsとか、なんでも読めるcatぐらいですね。

それでは、問題解説を終わります。